/*-----------------------------------------------------------------------------
 * 
 *
 *-----------------------------------------------------------------------------
 */

#include "common/include/stock_common.h"
#include "common/include/stock_update.h"
#include "server/include/stock_server.h"

pthread_t stock_server_thread;
stock_server_netstats_t stock_server_netstats;

static int server_sock = 0;


static void 
stock_server_init(uint8_t server_type)
{
    memset(&stock_server_netstats, 0, sizeof(stock_server_netstats_t));

    stock_server_netstats.server_type  = server_type;
    stock_server_netstats.dgram_stats  = &stock_server_dgram_stats;
    stock_server_netstats.stream_stats = &stock_server_stream_stats;
}

void
stock_server_shutdown(int arg)
{
    close(server_sock);

    if (arg == SIGINT) {
        /*
         * TODO: Do necessary cleanup
         */
        exit(1);
    }
}



void *
stock_server(void *arg)
{
    uint8_t            server_type;
    int                sock_type;
    int                proto_type;
    struct sockaddr_in server_addr;
    socklen_t          server_len = sizeof(server_addr);
    int                optval;
    int                rc = 0;
 
    /*
     * NOTE: In this routine we try to do the socket setup common to both dgram
     * and stream sockets (set up server address, set up/bind socket)
     */

    stock_server_init(*(uint8_t *) arg);

    server_type = stock_server_netstats.server_type;


    if (server_type == STOCK_SERVER_DGRAM) {
        sock_type = SOCK_DGRAM;
        proto_type = IPPROTO_UDP;
    } else if (server_type == STOCK_SERVER_STREAM) {
        sock_type = SOCK_STREAM;
        proto_type = IPPROTO_TCP;
    }

    /*
     * Initialize the server sockaddr structure to use any available IP address
     * and the desired server port
     */
    memset(&server_addr, 0, server_len);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(STOCK_SERVER_PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    /*
     * Create the socket that will be used for either receiving stock update 
     * messages from clients (server_type == dgram) or receiving new tcp 
     * connections from clients (server_type == stream) which will, in turn,
     * receive stock update messages from clients
     */
    if ((server_sock = socket(AF_INET, sock_type, proto_type)) < 0) {
        error("socket() call failed");
        return NULL;
    }

    /*
     * Set the SO_REUSEADDR flag so that restarting the server shortly after
     * a process kill will not result in bind error
     */
    rc = setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, 
                    &optval, sizeof(optval));

    /* 
     * Bind the socket to the server sockaddr
     */
    rc = bind(server_sock, (struct sockaddr *) &server_addr, server_len);

    if (rc < 0) {
        error("bind() call failed with return code: %d", rc);
        return NULL;
    }

    /*
     *
     */
    if (server_type == STOCK_SERVER_DGRAM) {
        stock_server_dgram(server_sock);
    } else if (server_type == STOCK_SERVER_STREAM) {
        stock_server_stream(server_sock);
    }   

    return NULL;
}

